{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 数据结构\n",
    "\n",
    "一旦我们开始一次性处理大量数据，采用数组（array）或者字典（dictionaries）这样的数据结构会比单纯依赖普通变量要方便得多。<br>\n",
    "\n",
    "要讲到的数据结构：\n",
    "1. 元组Tuples\n",
    "2. 字典Dictionaries\n",
    "3. 数组Arrays\n",
    "\n",
    "<br>\n",
    "总的来说，元组和数组都是元素的有序序列（所以可以直接索引元素）。字典和数组都是可变类型。\n",
    "下面进行详细一点的介绍！"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 元组Tuples\n",
    "\n",
    "通过用小括号`( )`将一个有序集合括起来可以创建一个元组。\n",
    "\n",
    "语法：<br>\n",
    "```julia\n",
    "(item1, item2, ...)```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "myfavoriteanimals = (\"penguins\", \"cats\", \"sugargliders\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们可以在这个元组里进行索引，"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "myfavoriteanimals[1]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "但因为元组是不可变对象，我们不可以改变它"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "myfavoriteanimals[1] = \"otters\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 在Julia1.0中：命名元组（NamedTuples）\n",
    "\n",
    "顾名思义，命名元组`NamedTuple`应该和元组`Tuple`差不多，只不过每个元素都有名字！命名元组的创建语法是在元组里使用`=`：\n",
    "\n",
    "```julia\n",
    "(name1 = item1, name2 = item2, ...)\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "myfavoriteanimals = (bird = \"penguins\", mammal = \"cats\", marsupial = \"sugargliders\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "就像元组`Tuples`，命名元组`NamedTuples`是有序的，所以也可以通过索引获得元素："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "myfavoriteanimals[1]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "命名元组还有特殊的通过元素名来访问元素的方法："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "myfavoriteanimals.bird"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 字典Dictionaries\n",
    "\n",
    "如果你的数据集有映射关系，可以考虑使用字典来存储数据。通过`Dict()`函数初始化字典，可以为空或者有键值对。\n",
    "\n",
    "语法：\n",
    "```julia\n",
    "Dict(key1 => value1, key2 => value2, ...)```\n",
    "\n",
    "典型的例子如通讯列表，它就是相关联的姓名和电话号码。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "myphonebook = Dict(\"Jenny\" => \"867-5309\", \"Ghostbusters\" => \"555-2368\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "在这个例子中，每对姓名和号码就是一个键值对。我们可以通过对应的键来获取Jenny的电话号码（值）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "myphonebook[\"Jenny\"]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们可以在这字典中继续增加条目"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "myphonebook[\"Kramer\"] = \"555-FILK\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "来瞧瞧现在这个电话本长什么样："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "myphonebook"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "使用`pop!`函数可以从通讯列表中删除Kramer，并同时获得他的电话号码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pop!(myphonebook, \"Kramer\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "myphonebook"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "与元组和数组不同，字典是无序的。所以不可通过索引获得元素。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "myphonebook[1]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "上面这个例子中，Julia会认为你想获得键为`1`的值。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 数组Arrays\n",
    "\n",
    "不同于元组，数组是可变的。不同于字典，数组是有序的。<br>\n",
    "使用方括号`[ ]`括起元素集合来创建数组。\n",
    "\n",
    "语法：<br>\n",
    "```julia\n",
    "[item1, item2, ...]```\n",
    "\n",
    "举个例子，我们可以创建一个数组来记录我的好友"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "myfriends = [\"Ted\", \"Robyn\", \"Barney\", \"Lily\", \"Marshall\"]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`Array{String,1}`中的`1`表示这是一个一维的向量。一个`Array{String,2}`是个二维矩阵，等等。`Array{String,1}`中的`String`是每个元素的类型。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "要存储一系列数字的话"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fibonacci = [1, 1, 2, 3, 5, 8, 13]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "mixture = [1, 1, 2, 3, \"Ted\", \"Robyn\"]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "获得数组后，我们可以通过索引来获得数组中的元素。如，想获得`myfriends`中的第三个成员，就可以写成"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "myfriends[3]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们可以通过索引来修个数组中的元素"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "myfriends[3] = \"Baby Bop\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " 是的，Julia的索引是从1开始的，而不像Python是从0开始的。这种小问题已经有很多争论了。我有个拥有Solomon智慧的朋友建议使用从½开始计数来一劳永逸地解决这个争端。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "函数`push!`和`pop!`也可以改变数组。`push!`在数组最后添加元素，而`pop!`删除数组的最后一个元素。\n",
    "\n",
    "给`fibonnaci`添加一个数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "push!(fibonacci, 21)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "再删掉它"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pop!(fibonacci)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fibonacci"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "到目前为止，我给的例子都是一维的标量，但其实数组可以有任意个维度、可以包含其他数组。\n",
    "<br><br>\n",
    "举个例子，下面这个数组就包含了数组："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "favorites = [[\"koobideh\", \"chocolate\", \"eggs\"],[\"penguins\", \"cats\", \"sugargliders\"]]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "numbers = [[1, 2, 3], [4, 5], [6, 7, 8, 9]]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "下面的例子是包含随机数的二维和三维数组。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "rand(4, 3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "rand(4, 3, 2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "当你复制数组的时候千万要小心！"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fibonacci"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "somenumbers = fibonacci"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "somenumbers[1] = 404"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fibonacci"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "修改`somenumbers`同时也修改了`fibonacci`！\n",
    "\n",
    "在上面那个例子中，实际上我们并没有获得`fibonacci`的副本。我们只是创建了一个新的获取`fibonacci`所绑定数组的元素的途径。\n",
    "\n",
    "如果想获得`fibonacci`所绑定数组的副本，可以使用`copy`函数。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 先复原fibonacci\n",
    "fibonacci[1] = 1\n",
    "fibonacci"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "somemorenumbers = copy(fibonacci)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "somemorenumbers[1] = 404"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fibonacci"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "上面这个例子中，fibonacci就没有受影响。因此可以判断`somemorenumbers`和`fibonacci`所绑定的数组是不同的。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "### 练习\n",
    "\n",
    "#### 3.1 \n",
    "用下面的代码创建一个数组`a_ray`：\n",
    "\n",
    "```julia\n",
    "a_ray = [1, 2, 3]\n",
    "```\n",
    "\n",
    "在这个数组的末尾添加数字`4`再删掉它。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "deletable": false,
    "editable": false,
    "hide_input": true,
    "nbgrader": {
     "checksum": "8dde7e020bc07c5762194abf5c72285c",
     "grade": true,
     "grade_id": "cell-a6b20b78fbfeb449",
     "locked": true,
     "points": 1,
     "schema_version": 1,
     "solution": false
    }
   },
   "outputs": [],
   "source": [
    "@assert a_ray == [1, 2, 3]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 3.2 \n",
    "试着通过下面的代码给`myphonebook`添加一个键为\"Emergency\"值为`string(911)`条目\n",
    "```julia\n",
    "myphonebook[\"Emergency\"] = 911\n",
    "```\n",
    "\n",
    "为啥不好使呢？"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 3.3 \n",
    "用如下代码创建一个名为`flexible_phonebook`的字典，其中Jenny的号码为整型，而Ghostbusters的号码为字符串\n",
    "\n",
    "```julia\n",
    "flexible_phonebook = Dict(\"Jenny\" => 8675309, \"Ghostbusters\" => \"555-2368\")\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "flexible_phonebook = Dict(\"Jenny\" => 8675309, \"Ghostbusters\" => \"555-2368\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "deletable": false,
    "editable": false,
    "hide_input": true,
    "nbgrader": {
     "checksum": "4a8e1e6d6c64cf1370e0fb60a233deb9",
     "grade": true,
     "grade_id": "cell-66f1d636c676c2f2",
     "locked": true,
     "points": 1,
     "schema_version": 1,
     "solution": false
    }
   },
   "outputs": [],
   "source": [
    "@assert flexible_phonebook == Dict(\"Jenny\" => 8675309, \"Ghostbusters\" => \"555-2368\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 3.4\n",
    "给`flexible_phonebook`添加一个键为\"Emergency\"值为整数`911`的条目。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "deletable": false,
    "editable": false,
    "hide_input": true,
    "nbgrader": {
     "checksum": "e790c96d2a64c04de1d9e01eaf5ba162",
     "grade": true,
     "grade_id": "cell-ee862ab938680259",
     "locked": true,
     "points": 1,
     "schema_version": 1,
     "solution": false
    }
   },
   "outputs": [],
   "source": [
    "@assert haskey(flexible_phonebook, \"Emergency\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "deletable": false,
    "editable": false,
    "hide_input": true,
    "nbgrader": {
     "checksum": "356ba6c4ee4d106b41aec07039bd8495",
     "grade": true,
     "grade_id": "cell-c8c40ababe7cdfce",
     "locked": true,
     "points": 2,
     "schema_version": 1,
     "solution": false
    }
   },
   "outputs": [],
   "source": [
    "@assert flexible_phonebook[\"Emergency\"] == 911"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 3.5\n",
    "为什么`flexible_phonebook`可以添加值为整型的条目而`myphonebook`不行？该如何初始化`myphonebook`才能添加值为整型的条目呢？"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "完成练习后请点击顶部的`Validate`"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Julia 1.0.5",
   "language": "julia",
   "name": "julia-1.0"
  },
  "language_info": {
   "file_extension": ".jl",
   "mimetype": "application/julia",
   "name": "julia",
   "version": "1.0.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
